﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Platform.Infrastructure;
using System.Runtime.Serialization;

namespace Platform.ObjectModel
{
	/// <summary>
	/// 
	/// </summary>
	[Serializable]
	public class DataObjectName : ISerializable
	{
		/// <summary>
		/// Namespace padrão do sistema
		/// </summary>
		public const string DefaultNamespace = "Default";

		/// <summary>
		/// Obtém para representa um documento do sistema sem nome
		/// </summary>
		public static readonly DataObjectName Unknow = new DataObjectName( "unkown" );

		/// <summary>
		/// 
		/// </summary>
		internal const string RuleNamespace = DefaultNamespace + ".Rules";
		
		/// <summary>
		/// Separador inicial entre o namespace e nome
		/// </summary>
		private const char InitialNamespaceSeparator = '[';
		/// <summary>
		/// Separador final do namespace e nome
		/// </summary>
		private const char FinalNamespaceSeparator = ']';


		private string fullName;

		#region [ .ctor ]

		public DataObjectName( string name )
			: this( name, DefaultNamespace )
		{ }

		public DataObjectName( string name, string ns )
		{
			Arguments.IsNotNullOrEmpty( "name", name );
			//Arguments.IsNotNullOrEmpty( "ns", ns );

			this.Name = name;
			this.Namespace = String.IsNullOrEmpty( ns ) ? DefaultNamespace : ns;
		}

		#endregion

		/// <summary>
		/// Obtém o nome que identifica o objeto
		/// </summary>
		public virtual string Name
		{
			get;
			protected set;
		}

		/// <summary>
		/// Obtém o namespace do objeto
		/// </summary>
		public virtual string Namespace
		{
			get;
			protected set;
		}

		/// <summary>
		/// Indica se é o namespace padrão do sistema
		/// </summary>
		internal bool IsNamespaceDefault
		{ get { return String.Equals( this.Namespace, DefaultNamespace ); } }

		#region IDataObjectName Members

		/// <summary>
		/// Obtém o nome completo que identifica únicamente o objeto
		/// </summary>
		public string FullName
		{
			get
			{
				if ( this.fullName == null )
					this.fullName = String.Concat( this.Namespace, ".", this.Name );
				return this.fullName;
			}
		}

		#endregion

		public override string ToString()
		{
			return this.FullName;
		}

		public override bool Equals( object obj )
		{
			DataObjectName dataName = obj as DataObjectName;

			if ( dataName == null )
				return false;
			else
				return this.FullName.Equals( dataName.FullName, StringComparison.InvariantCultureIgnoreCase );
		}

		public override int GetHashCode()
		{
			return this.FullName.GetHashCode();
		}

		//internal string FieldName( string field )
		//{
		//    return String.Concat( this.Namespace, ".", this.Name, "[", field, "]" );
		//}

		/// <summary>
		/// Obtém o nome baseado no nome da classe
		/// </summary>
		/// <param name="typeName"></param>
		/// <param name="ns"></param>
		/// <returns></returns>
		internal static DataObjectName From( Type typeName )
		{
			Arguments.IsNotNull( "typeName", typeName );
			return new DataObjectName( typeName.Name );	
		}

		/// <summary>
		/// Realiza o parse do <paramref name="fullName"/> para o objeto que representa o nome do objeto
		/// </summary>
		/// <param name="fullName"></param>
		/// <returns></returns>
		public static DataObjectName Parse( string fullName )
		{
			DataObjectName name;
			if ( DataObjectName.TryParse( fullName, out name ) )
				return name;
			else
				throw new InvalidCastException( String.Format( "Não foi possível converter o nome \"{0}\" para o tipo \"DataObjectName\"." ) );
		}

		/// <summary>
		/// Realiza o parse de <paramref name="fullName"/>
		/// </summary>
		/// <param name="fullName"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		//[Obsolete( "Mudar o formato do parser DataObjctName") ]
		public static bool TryParse( string fullName, out DataObjectName name )
		{
			name = null;
			if ( fullName == null )
				return false;

			int lastDot = fullName.LastIndexOf( '.' );
			if ( lastDot <= 0 )
				return false;

			name = new DataObjectName( fullName.Substring( lastDot + 1 ), fullName.Substring( 0, lastDot ) );
			return true;

			//name = null;

			//if ( String.IsNullOrEmpty( fullName ) )
			//    return false;

			//// Obtém a posição do nome
			//int startAt = fullName.IndexOf( InitialNamespaceSeparator );
			//int endAt = fullName.IndexOf( FinalNamespaceSeparator ) - 1;

			//// Se o namespace está vazio ou o nome está vazio, não é um DataObjectName válido
			//if ( startAt <= 0 || endAt <= startAt )
			//    return false;

			//name = new DataObjectName( fullName.Substring( startAt + 1, endAt - startAt ), fullName.Substring( 0, startAt ) );
			//return true;
		}

		/// <summary>
		/// Retorna o nome completo para acessar o campo <paramref name="qualifiedName"/> do documento
		/// </summary>
		/// <param name="qualifiedName"></param>
		/// <returns></returns>
		public string FieldName( DataObjectName qualifiedName )
		{
			Arguments.IsNotNull( "fieldName", qualifiedName );

			if ( String.Equals( this.FullName, qualifiedName.Namespace, StringComparison.InvariantCultureIgnoreCase ) )
				return qualifiedName.Name;
			else
				return qualifiedName.FullName;
		}

		/// <summary>
		/// Retorna o nome que identifica unicamente um campo na biblioteca de documentos
		/// </summary>
		/// <param name="fieldName"></param>
		/// <returns></returns>
		public string FieldNameWithSchema( string fieldName )
		{
			Arguments.IsNotNullOrEmpty( "fieldName", fieldName );
			return String.Format( "{0}.{1}[{2}]", this.Namespace, this.Name, fieldName ).ToLowerInvariant();
		}


		/// <summary>
		/// Retorna o nome que identifica unicamento um campo na biblioteca de documentos
		/// </summary>
		/// <param name="qualifiedName"></param>
		/// <returns></returns>
		public string FieldNameWithSchema( DataObjectName qualifiedName )
		{
			return this.FieldNameWithSchema( this.FieldName( qualifiedName ) );
		}

		#region ISerializable Members

		public void GetObjectData( SerializationInfo info, StreamingContext context )
		{
			throw new NotImplementedException();
		}

		#endregion

		#region [ Operators ]

		//public static override bool operator ==( DataObjectName name1, DataObjectName name2 )
		//{
		//    if ( name1 == null || name2 == null )
		//        return false;
		//    else
		//        return name1.Equals( name2 );
		//}

		//public static override bool operator !=( DataObjectName name1, DataObjectName name2 )
		//{
		//    return name1 == null || name2 == null ? false : !name1.Equals( name2 );
		//}

		#endregion
	}

}
